/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package it.txt.access.capability.finder;

import it.txt.access.capability.commons.filter.CapabilityFileNameFilter;
import java.io.File;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import javax.xml.xquery.XQConnection;
import javax.xml.xquery.XQDataSource;
import javax.xml.xquery.XQException;
import javax.xml.xquery.XQExpression;
import javax.xml.xquery.XQResultSequence;
import org.w3c.dom.Element;

/**
 *
 * @author seccia
 */
public class CapabilityXQueryBXSearch {

    private static final Logger LOGGER = Logger.getLogger(CapabilityXQueryBXSearch.class.getName());
    //This parte of query is used to declare the capability namespace and
    //also the query's variables
    private static final String DRIVER_BX = "org.basex.api.xqj.BXQDataSource";    
    private static final String resourceIDRegex = "";
    private static final String declaration = ""
            + "declare namespace tns = 'http://schema.capability.access.txt.it';"
            + "declare variable $capabilityDoc as xs:string external;"
            + "declare variable $subjectID as xs:string external;"
            + "declare variable $resourceID as xs:string external;"
            + "declare variable $permittedAction as xs:string external;";
    //This part of query is used to retrieve the correct cabability based
    //on the requested parameters.
    private static final String query = ""
            + " let $c := doc($capabilityDoc)/tns:AccessRightsCapability"
            + " let $existPermittedAction"
            + " := index-of($c/tns:AccessRights/tns:AccessRight/tns:PermittedAction, "
            + " $permittedAction)"
            + " where $c/tns:Subject/tns:SubjectID eq $subjectID"
            + " and $c/tns:ResourceID eq $resourceID"
            + " and $existPermittedAction != 0"
            + " return $c";
    //The xqdata source connection
    private XQConnection xQConnection;
    //Connection enstablished
    private boolean connected;
    //The directory containing the xml capabilities
    private File capabilitiesDir;

    public CapabilityXQueryBXSearch(File capabilitiesDir) {
        if (!capabilitiesDir.isDirectory()) {
            throw new IllegalArgumentException("No Capabilities Directory specified.");
        }
        try {
            xQConnection = ((XQDataSource) Class.forName(DRIVER_BX).newInstance()).getConnection();
            this.connected = (xQConnection != null) ? true : false;
            this.capabilitiesDir = capabilitiesDir;
        } catch (Exception ex) {
            this.connected = false;
            LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
            // free all resources allocated for the connection
            if (xQConnection != null) {
                try {
                    xQConnection.close();
                } catch (XQException xqe) {
                    LOGGER.log(Level.SEVERE, xqe.getMessage(), xqe);
                }
            }
        }
    }

    public ArrayList<CapabilitySearchReturn> doSearchCapability(String userSubject, String userResourceID, String userPermittedAction) {

        ArrayList<CapabilitySearchReturn> result = new ArrayList<CapabilitySearchReturn>();

        boolean declare = true;
        XQExpression expr = null;
        XQResultSequence xqrs = null;

        try {

            expr = xQConnection.createExpression();

            QName subjectIDVar = new QName("subjectID");
            expr.bindString(subjectIDVar, userSubject, null);
            QName resourceIDVar = new QName("resourceID");
            expr.bindString(resourceIDVar, userResourceID, null);
            QName permitedActionVar = new QName("permittedAction");
            expr.bindString(permitedActionVar, userPermittedAction, null);

            for (String fileName : capabilitiesDir.list(new CapabilityFileNameFilter())) {

                File capabilityFile = new File(capabilitiesDir, fileName);

                LOGGER.log(Level.INFO, "Searching in file: {0}", capabilityFile.getAbsolutePath());

                QName documentVar = new QName("capabilityDoc");
                expr.bindString(documentVar, capabilityFile.toURI().toString(), null);

                // the XQuery expression to be executed
                String expression = declare ? declaration + query : query;

                // execute the XQuery expression
                xqrs = expr.executeQuery(expression);

                // process the result (sequence) iteratively
                while (xqrs.next()) {
                    // retrieve the current capability
                    Element capability = (Element) xqrs.getObject();
                    String capabilityID = capability.getAttribute("AccessRightsCapabilityID");
                    String capabilityIssueIstant = capability.getAttribute("IssueInstant");
                    CapabilitySearchReturn csr = new CapabilitySearchReturn();
                    csr.setCapabilityIssueDate(capabilityIssueIstant);
                    csr.setCapabilityID(capabilityID);
                    result.add(csr);
                    // Print out the retrieved capability
                    //CapabilityPrinter.prettyLogDocumentElement(capability, "OK Capability: " + fileName);
                }

                //clore the result set
                xqrs.close();

                //No more declaration
                declare = false;
            }
        } catch (XQException ex) {
            // handle the exception in some manner
            LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
        } // free the resources whether or not there was an exception
        finally {
            // free all resources allocated for the result
            if (xqrs != null) {
                try {
                    xqrs.close();
                } catch (XQException ex) {
                    LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
                }
            }
            // free all resources allocated for the expression
            if (expr != null) {
                try {
                    expr.close();
                } catch (XQException ex) {
                    LOGGER.log(Level.SEVERE, ex.getMessage(), ex);
                }
            }
        }
        return result;
    }

    public boolean isAlive() {
        return connected;
    }

    /*public static void main(String[] args) {

        File capabilitiesDir = new File("D:/Capabilities");

        String resourceID = "file:/D:/Access/Capability_Keystore/cert_req.txt";
        String subject = "alice@acme.com";
        String operation = "Create";

        CapabilityXQueryBXSearch cs = new CapabilityXQueryBXSearch(capabilitiesDir);

        if (cs.isAlive()) {

            ArrayList<CapabilitySearchReturn> capabilities = cs.doSearchCapability(subject, resourceID, operation);

            Collections.sort(capabilities, new Comparator<CapabilitySearchReturn>() {

                public int compare(CapabilitySearchReturn o1, CapabilitySearchReturn o2) {
                    XMLGregorianCalendar issueDate1 = o1.getCapabilityIssueDateToXmlGregorianCalendar();
                    XMLGregorianCalendar isssueDate2 = o2.getCapabilityIssueDateToXmlGregorianCalendar();
                    return issueDate1.compare(isssueDate2);
                }
            });

            LOGGER.log(Level.INFO, "\nRetrieved Capabilities - Sort By Date \n");

            for (CapabilitySearchReturn capabilitySearchReturn : capabilities) {
                LOGGER.log(Level.INFO, "Capability ID: {0}", capabilitySearchReturn.getCapabilityID());
            }
        }
    }*/
}
